package com.hdx;

import org.apache.log4j.Logger;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

/**
 * @Auther: hdx
 * @Date: 2019/1/29 11:30
 * @Description:
 */
public class DistributedLock {
    private static Logger logger = Logger.getLogger(TestZookeeper.class);
    private static String connectString = "10.0.0.114:2181,10.0.0.115:2181,10.0.0.116:2181";
    private static int sessionTimeout = 2000;//毫秒
    private static ZooKeeper zooKeeper;
    private static final String PARENTPATH = "/dLocks";//存放锁的父节点

    CountDownLatch cd = new CountDownLatch(1);

    /**
     * 初始化zookeeper链接对象
     */
    public DistributedLock() throws Exception {
        {
            //多长时间链接不上zk了就会session会话超时
            /**
             * 建立和zookeeper集群的连接，操作都是用它
             */
            zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
                //这个是wathcher的回调方法：跟ajax的回调是一个意思，连上后会回调到这个方法里
                public void process(WatchedEvent watchedEvent) {
                    System.out.println("事件回调：" + watchedEvent.getState());
                    if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { //如果异步连接成功了
                        cd.countDown();
                    }
                }
            });
            cd.await(); //给他手动卡住，不让事件这个方法结束，等待事件回调
            //   System.out.println("await了");
        }
    }

    /**
     * 申请锁（饿汉式的，客户端得2秒一次去zk查询这个锁有没有释放）
        当客户端很多的时候，那zk就搞死了
     */

    public void getLock(String ITem_id) throws Exception {
        //1、创建父节点检查是否存在
        Stat exists = zooKeeper.exists(PARENTPATH, false);
        if (exists == null) {
            zooKeeper.create(PARENTPATH, "this is a distributed parent_path".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  //用持久化的节点
        }
        //2、创建带有唯一标识的子节点
        String lock_Path = PARENTPATH + "/"+ITem_id;
        int tryMay=10; //最多重试次数
        int tryTime=2000; //重试间隔时间
        int tryCount=1;//当前重试次数
        try { //尝试首次创建是否成功
            //子节点就创建临时节点，
            //因为临时节点会跟会话同时存在，要是进程挂了（客户端挂了），那这个节点就消失了
            zooKeeper.create(lock_Path, "this is a distributed children_path".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
        }catch (Exception e){
            //如果创建不成功，重试创建（二次以上尝试创建）
            while (tryCount<tryMay){
                Thread.sleep(tryTime);
                tryCount=tryCount+1;
                try {
                    zooKeeper.create(lock_Path, "this is a distributed children_path".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
                }catch (Exception e2){
                    System.out.println("锁子节点创建失败尝试次数："+tryCount);
                    continue; //如果catch里面创建都失败了就不尝试了，否则会死循环
                }
                System.out.println("锁子节点创建成功尝试次数："+tryCount);
                break; //如果创建成功就跳出while循环了
            }

        }

    }

    /**
     * 释放锁
     */
    public void releaseLock(String ITem_id) throws Exception{
        System.out.println("释放锁了");
        zooKeeper.delete(PARENTPATH+"/"+ITem_id,-1);
    }
}
